package de.core.gameplay.actor.misc.button;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;

import de.core.GameFrame;
import de.core.controlls.ButtonListener;
import de.core.gameplay.actor.AbstractActor;

public class Button extends AbstractActor {
    private final BufferedImage defaultImage;
    private final BufferedImage mouseOverImage;
    private final BufferedImage mousePressedImage;

    private ButtonState buttonState;
    private List<ButtonListener> listeners;


    public Button(BufferedImage aImage, int aX, int aY) {
        this(aImage, aImage, aX, aY);
    }


    public Button(BufferedImage aImage, BufferedImage aMouseOverImage, int aX, int aY) {
        this(aImage, aMouseOverImage, aImage, aX, aY);
    }

    public Button(BufferedImage aImage, BufferedImage aMouseOverImage, BufferedImage aButtonPressedImage, int aX, int aY) {
        super(aImage, aX, aY);
        defaultImage = aImage;
        mouseOverImage = aMouseOverImage;
        mousePressedImage = aButtonPressedImage;
        buttonState = ButtonState.DEFAULT;

        listeners = new ArrayList<ButtonListener>();

        initEventHandling();
    }

    private void initEventHandling() {
        MouseHandler mouseHandler = new MouseHandler();
        GameFrame.getInstance().addMouseMotionListener(mouseHandler);
        GameFrame.getInstance().addMouseListener(mouseHandler);

        addButtonListener(new ButtonStateUpdater());
    }

    // Public API **************************************************************

    @Override
    public void act() {
        toggleImageByButtonState();
    }

    public void addButtonListener(ButtonListener aListener) {
        listeners.add(aListener);
    }

    public void removeButtonListener(ButtonListener aListener) {
        listeners.remove(aListener);
    }

    // Private API *************************************************************

    private void toggleImageByButtonState() {
        switch (buttonState) {
            case DEFAULT:
                setImage(defaultImage);
                break;
            case MOUSE_OVER:
                setImage(mouseOverImage);
                break;
            case PRESSED:
                setImage(mousePressedImage);
                break;
            default:
                throw new IllegalStateException("Button has unknown Image state!");
        }
    }


    // Inner Classes ***********************************************************

    private class ButtonStateUpdater implements ButtonListener {

        @Override
        public void buttonClicked() {
        }

        @Override
        public void mouseEnteredButton() {
            buttonState = ButtonState.MOUSE_OVER;
        }

        @Override
        public void mouseExitedButton() {
            buttonState = ButtonState.DEFAULT;
        }

        @Override
        public void buttonPressed() {
            buttonState = ButtonState.PRESSED;
        }

        @Override
        public void buttonReleased(boolean aIsMouseOverButton) {
            if (aIsMouseOverButton) {
                buttonState = ButtonState.MOUSE_OVER;
            } else {
                buttonState = ButtonState.DEFAULT;
            }
        }
    }

    private class MouseHandler implements MouseListener, MouseMotionListener {
        private Point2D lastMousePosition;

        public MouseHandler() {
            lastMousePosition = new Point2D.Double();
        }

        @Override
        public void mouseClicked(MouseEvent aEvent) {
            if (!isPointOnButton(aEvent.getPoint())) {
                return;
            }

            for (ButtonListener listener : listeners) {
                listener.buttonClicked();
            }
        }

        @Override
        public void mouseMoved(MouseEvent aEvent) {
            if (!isPointOnButton(lastMousePosition) && isPointOnButton(aEvent.getPoint())) {
                for (ButtonListener listener : listeners) {
                    listener.mouseEnteredButton();
                }
            } else if (isPointOnButton(lastMousePosition) && !isPointOnButton(aEvent.getPoint())) {
                for (ButtonListener listener : listeners) {
                    listener.mouseExitedButton();
                }
            }
            lastMousePosition = aEvent.getPoint();
        }

        private boolean isPointOnButton(Point2D aPoint) {
            double upperLeftX = getLocation().getX() - getImage().getWidth(null) / 2;
            double topY = getLocation().getY() - getImage().getHeight(null) / 2;
            return  aPoint.getX() >= upperLeftX && aPoint.getX() <= upperLeftX + getImage().getWidth(null)
                    && aPoint.getY() >= topY && aPoint.getY() <= topY + getImage().getHeight(null);
        }

        @Override
        public void mouseEntered(MouseEvent aEvent) {
        }

        @Override
        public void mouseExited(MouseEvent aEvent) {
        }

        @Override
        public void mousePressed(MouseEvent aEvent) {
            if (!isPointOnButton(aEvent.getPoint())) {
                return;
            }

            for (ButtonListener listener : listeners) {
                listener.buttonPressed();
            }
        }

        @Override
        public void mouseReleased(MouseEvent aEvent) {
            for (ButtonListener listener : listeners) {
                listener.buttonReleased(isPointOnButton(aEvent.getPoint()));
            }
        }

        @Override
        public void mouseDragged(MouseEvent aEvent) {
        }
    }
}
